package org.stream.answerduel.service;

import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.stream.answerduel.util.SnowflakeIdWorker;
import org.stream.answerduel.vo.MatchingUser;
import org.stream.answerduel.vo.Message.PvpMessage;
import org.stream.answerduel.vo.Room;
import org.stream.answerduel.vo.*;

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Controller
@Slf4j
public class MatchThread implements DisposableBean, Runnable {
    private Thread thread;
    //private Random random = new Random();
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;

    @Autowired
    private MQService mqService;

    @Autowired
    private UserService userService;

    @Autowired
    private SubjectService subjectService;

    @Value("${max-wait-time}")
    private int maxWaitTime = 3000;

    // 匹配队列，使用线程安全的哈希表
    public static Map<Long, MatchingUser> matchQueue = new ConcurrentHashMap<Long, MatchingUser>();
    public static Map<Long, Room> roomList = new ConcurrentHashMap<Long, Room>();
    public static Map<String, WaitingRoom> waitingRoomList = new ConcurrentHashMap<String, WaitingRoom>();

    public MatchThread() {
        thread = new Thread(this);
        thread.start();
    }

    public void run() {
        while (true) {
            // for (int i = 0; i < matchQueue.size(); i++)
            // {
            // MatchingUser user = matchQueue.get(i);
            // user.setPriority(user.getPriority() + 1);
            // user.setWaitTime(user.getWaitTime() + 1);
            // tryToFindRival(user);
            // }
            
            try 
            {
                Iterator<Map.Entry<Long, MatchingUser>> iterator = matchQueue.entrySet().iterator();
                while (iterator.hasNext())
                {
                    Map.Entry<Long, MatchingUser> entry = iterator.next();
                    MatchingUser user = entry.getValue();
                    if(user.getPriority() == -1)
                        continue;
                    user.update();
                    if (tryToFindRival(user))
                        break;
                }
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void addQueue(MatchingUser user) {
        if (isInQueue(user.getId()) == false) // 不在队列中才能加入
            matchQueue.put(user.getId(), user);
    }

    public boolean isInQueue(long id){
        if(matchQueue.containsKey(id))
            return true;
        else
            return false;
    }

    public boolean removeQueue(MatchingUser user){
        if(matchQueue.containsKey(user.getId()))
        {
            matchQueue.remove(user.getId());
            return true;
        }else
            return false;
    }

    public boolean removeQueue(Long id){
        if(matchQueue.containsKey(id))
        {
            matchQueue.remove(id);
            return true;
        }else
            return false;
    }

    public String createWaitingRoom(UserVo userVo) {
        String roomID = String.valueOf(System.currentTimeMillis()).substring(7, 13);
        while (waitingRoomList.containsKey(roomID)) {
            roomID = String.valueOf(System.currentTimeMillis()).substring(7, 13);
        }
        WaitingRoom waitingRoom = new WaitingRoom();
        waitingRoom.create(roomID, userVo,mqService,userService,subjectService);
        waitingRoomList.put(roomID, waitingRoom);
        log.info("用户ID：" + userVo.getId()+"创建好友房间成功，房间号：" + roomID);
        return roomID;
    }

    /**
     * 功能描述：用户加入房间
     * @param userVo 需要加入的用户
     * @param roomID 房间号
     * @return 加入后的房间对象，如果房间已满则返回null
     */
    public WaitingUser joinWaitingRoom(UserVo userVo, String roomID) {
        WaitingRoom waitingRoom = waitingRoomList.get(roomID);
        WaitingUser waitingUser = waitingRoom.join(userVo);
        if(waitingUser == null)
            log.error("房间已满，或好友已离开");
        else
            log.info("用户ID：" + userVo.getId()+"加入好友房间成功，房间号：" + roomID);
        return waitingUser;
    }

    public void exitWaitingRoom(long userID, String roomID){
        if(waitingRoomList.containsKey(roomID))
        {
            WaitingRoom waitingRoom = waitingRoomList.get(roomID);
            waitingRoom.exit(userID);
            log.info("用户："+userID+"退出好友房间，房间号："+roomID);
            if (waitingRoom.isEmpty() == true) {
                waitingRoomList.remove(roomID);
                log.info("好友房间:"+roomID+"已经空了，自动销毁");
            }
        }
    }

    public void forceExitWaitingRoom(long id){
        Iterator<Map.Entry<String, WaitingRoom>> iterator = waitingRoomList.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, WaitingRoom> entry = iterator.next();
            WaitingRoom waitingRoom = entry.getValue();
            if (waitingRoom.hasUser(id)){
                waitingRoom.exit(id);
                log.info("用户："+id+"强制退出好友房间，房间号："+waitingRoom.getRoomID());
                if (waitingRoom.isEmpty() == true) {
                    waitingRoomList.remove(waitingRoom.getRoomID());
                    log.info("好友房间:"+waitingRoom.getRoomID()+"已经空了，自动销毁");
                }
                return;
            }
        }
    }

//    public void forceExitRoom(long id){
//        Iterator<Map.Entry<String, Room>> iterator = roomList.entrySet().iterator();
//        while (iterator.hasNext()) {
//            Map.Entry<String, Room> entry = iterator.next();
//            Room room = entry.getValue();
//            if (room.hasUser(id)){
//                room.exit(id);
//                log.info("用户："+id+"强制退出对战房间，房间号："+room.getRoomID());
//                return;
//            }
//        }
//    }

    public boolean isInGame(long id){
        Iterator<Map.Entry<Long, Room>> iterator = roomList.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Long, Room> entry = iterator.next();
            Room room = entry.getValue();
            if (room.hasUser(id) && room.isStart())
                return true;
        }
        return false;
    }

    public boolean isInAnyWaitingRoom(long id){
        Iterator<Map.Entry<String, WaitingRoom>> iterator = waitingRoomList.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, WaitingRoom> entry = iterator.next();
            WaitingRoom waitingRoom = entry.getValue();
            if (waitingRoom.hasUser(id))
                return true;
        }
        return false;
    }

    public boolean isRoomExist(Long roomID){
        if(roomList.containsKey(roomID))
            return true;
        else
            return false;
    }

    public boolean isWaitingRoomExist(String roomID){
        if(waitingRoomList.containsKey(roomID))
            return true;
        else
            return false;
    }

    public boolean isUserInRoom(long userID,Long roomID){
        boolean has = false;
        if(roomList.containsKey(roomID))
        {
            Room room = roomList.get(roomID);
            has = room.hasUser(userID);
        }
        return has;
    }

    public boolean isUserInWaitingRoom(long userID,String roomID){
        boolean has = false;
        if(waitingRoomList.containsKey(roomID))
        {
            WaitingRoom waitingRoom = waitingRoomList.get(roomID);
            has = waitingRoom.hasUser(userID);
        }
        return has;
    }

    public boolean isWaitingRoomFull(String roomID){
        boolean isFull = false;
        if(waitingRoomList.containsKey(roomID))
        {
            WaitingRoom waitingRoom = waitingRoomList.get(roomID);
            isFull = waitingRoom.isFull();
        }
        return isFull;
    }

    // @Async
    /**
     * 尝试寻找该用户的对手
     * 
     * @param user
     */
    private synchronized boolean tryToFindRival(MatchingUser user)
    {
        //MatchingUser user = matchQueue.get(index);
        //int min = Integer.MAX_VALUE;
        //int rivalIndex = 0;
        
        // for (int i = 0; i < matchQueue.size(); i++) 
        // {
        //     MatchingUser rival = matchQueue.get(i);
        //     if (rival.getId() == user.getId())//不能匹配自己
        //         continue;
        //     int difference = Math.abs(user.getScore() - rival.getScore());//计算自己和对手的分数差
        //     if (difference < min) {
        //         min = difference;
        //         //rivalIndex = i;
        //         bestRival = rival; //分数差距最小的对手
        //     }
        // }

        MatchingUser bestRival = null;//需要匹配的实力接近的对手，初始化为null
        Iterator<Map.Entry<Long, MatchingUser>> iterator = matchQueue.entrySet().iterator();
        while (iterator.hasNext()) 
        {
            Map.Entry<Long, MatchingUser> entry = iterator.next();
            MatchingUser rival = entry.getValue();
            if (rival.getId() == user.getId())//不能匹配自己
                continue;
            if (rival.getPriority() == -1 && user.getWaitTime() < maxWaitTime)//等待时间超过设定的时长才匹配机器人
                continue;
            int difference = Math.abs(user.getScore() - rival.getScore());//计算自己和对手的分数差
            if (difference <= user.getDiff()) {
                //min = difference;
                //rivalIndex = i;
                bestRival = rival; //分数差距小于一定数字的对手
                break;
            }
        }
        if(bestRival != null)
        {
            synchronized (thread)
            {
                Room room = new Room();
                Long roomId=snowflakeIdWorker.nextId();
                room.init(roomId,user,bestRival,mqService,userService,subjectService);
                roomList.put(roomId, room);
                matchQueue.remove(user.getId());//移除用户
                matchQueue.remove(bestRival.getId());//移除匹配到的对手
                log.info("匹配成功，房间ID："+room.getRoomID());
                log.info("用户ID："+user.getId()+" 对手ID:"+bestRival.getId());
                
               try {
                   PvpMessage pvpMessage = new PvpMessage(bestRival, roomId);
                   mqService.publish(pvpMessage.toString(), "/public/TEST/"+user.getWechatID());
                   log.info(pvpMessage.toString()+user.getWechatID());

                   pvpMessage = new PvpMessage(user, roomId);
                   mqService.publish(pvpMessage.toString(), "/public/TEST/"+bestRival.getWechatID());
                   log.info(pvpMessage.toString()+bestRival.getWechatID());
               } catch (MqttException e) {
                   e.printStackTrace();
               }
               
                return true;
            }
            
        }
        else
            return false;
    }

    public AnswerResultVo answer(Long id, Long roomID, int choice)
    {
        try 
        {
            if (roomList.containsKey(roomID)) {
                Room room = roomList.get(roomID);
                if (id.equals(room.getPlayingUsers().get(0).getId())) {
                    return room.setUserChoice(0, choice);
                } else if (id.equals(room.getPlayingUsers().get(1).getId())) {
                    return room.setUserChoice(1, choice);
                }
            }
        } 
        catch (Exception e) 
        {
            e.printStackTrace();
        }
        return null;
    }

    public void ready(Long id, Long roomID)
    {
        try 
        {
            if (roomList.containsKey(roomID)) {
                Room room = roomList.get(roomID);
                if (id.equals(room.getPlayingUsers().get(0).getId())) {
                    room.setReady(0);
                } else if (id.equals(room.getPlayingUsers().get(1).getId())) {
                    room.setReady(1);
                }
            }
        } 
        catch (Exception e) 
        {
            e.printStackTrace();
        }
    }

    public void prepare(Long id, String roomID)
    {
        try
        {
            if (waitingRoomList.containsKey(roomID)) {
                WaitingRoom waitingRoom = waitingRoomList.get(roomID);
                if (id.equals(waitingRoom.getWaitingUsers().get(0).getId())) {
                    waitingRoom.setPrepare(0);
                } else if (id.equals(waitingRoom.getWaitingUsers().get(1).getId())) {
                    waitingRoom.setPrepare(1);
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    public void cancelPrepare(Long id, String roomID)
    {
        try
        {
            if (waitingRoomList.containsKey(roomID)) {
                WaitingRoom waitingRoom = waitingRoomList.get(roomID);
                if (id.equals(waitingRoom.getWaitingUsers().get(0).getId())) {
                    waitingRoom.cancelPrepare(0);
                } else if (id.equals(waitingRoom.getWaitingUsers().get(1).getId())) {
                    waitingRoom.cancelPrepare(1);
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    public Long findRoomIDByUser(Long id){
        Iterator<Map.Entry<Long, Room>> iterator = roomList.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Long, Room> entry = iterator.next();
            Room room = entry.getValue();
            if (room.hasUser(id) && room.isStart())
                return room.getRoomID();
        }
        return null;
    }

    public ReconnectVo reconnect(Long id,Long roomID){
        Room room = roomList.get(roomID);
        UserVo me = new UserVo();
        UserVo rival = new UserVo();
        if (id.equals(room.getPlayingUsers().get(0).getId())) {
            BeanUtils.copyProperties(room.getPlayingUsers().get(0), me);
            BeanUtils.copyProperties(room.getPlayingUsers().get(1), rival);
        } else {
            BeanUtils.copyProperties(room.getPlayingUsers().get(1), me);
            BeanUtils.copyProperties(room.getPlayingUsers().get(0), rival);
        }
        ReconnectVo reconnectVo = new ReconnectVo();
        reconnectVo.setMe(me);
        reconnectVo.setRival(rival);
        reconnectVo.setRoomId(roomID);
        int currentIndex = room.getCurrentIndex();
        reconnectVo.setCurrentIndex(currentIndex);
        reconnectVo.setSubject(room.getSubjects().get(currentIndex));
        return reconnectVo;
    }

    @Override
    public void destroy() throws Exception {

    }
}